# ifndef __Wrappers_H_
# define __Wrappers_H_

class CDestroyer {
	CDestroyer (const CDestroyer &);
	void operator = (const CDestroyer &);

  public:
	CDestroyer (void (* _pfDestroy) ()) : m_pfDestroy (_pfDestroy) {}
	~CDestroyer () {m_pfDestroy ();}

  private:
	void (* m_pfDestroy) ();
};

# define INVOKE_AT_EXIT(_f) CDestroyer destroyer_##_f (_f)

/////////////////////////////////////////////////////

//
// Heap-related wrappers.
//

template<typename T>
class CObjectHolder {
	// Disable copy semantics.
	CObjectHolder (const CObjectHolder &);
	void operator = (const CObjectHolder &);

  public:
	CObjectHolder (const T * _p) : m_p (_p) {}
	~CObjectHolder () {delete const_cast<T *> (m_p);}

	void Detach () {m_p = NULL;}
	void Free () {delete const_cast<T *> (m_p); m_p = NULL;}

  private:
	const T * m_p;
};

template<typename T>
class CArrayHolder {
	// Disable copy semantics.
	CArrayHolder (const CArrayHolder &);
	void operator = (const CArrayHolder &);

  public:
	CArrayHolder (const T * _p) : m_p (_p) {}
	~CArrayHolder () {delete [] const_cast<T *> (m_p);}

	void Detach () {m_p = NULL;}
	void Free () {delete [] const_cast<T *> (m_p); m_p = NULL;}

  private:
	const T * m_p;
};

typedef CArrayHolder<BYTE> CByteArrayHolder;

////////////////////////////////////

class CGlobalPtr {
  public:
	CGlobalPtr (size_t _cSize) :
		m_hGlobal (::GlobalAlloc (GMEM_MOVEABLE, _cSize)),
		m_pPtr (NULL)
	{}
	~CGlobalPtr () {
		if (m_pPtr)
			::GlobalUnlock (m_hGlobal);
		if (m_hGlobal)
			::GlobalFree (m_hGlobal);
	}

	bool operator ! () const {return m_hGlobal == NULL;}
	operator HGLOBAL () const {return m_hGlobal;}

	void * Lock () {
		if (! m_pPtr)
			m_pPtr = ::GlobalLock (m_hGlobal);
		return m_pPtr;
	}

	HGLOBAL Detach () {
		const HGLOBAL hGlobal = m_hGlobal;
		m_hGlobal = NULL;
		m_pPtr = NULL;
		return hGlobal;
	}

  private:
	HGLOBAL m_hGlobal;
	void * m_pPtr;
};

////////////////////////////////////

//
// COM-related wrappers.
//

class CCom {
  public:
	CCom () {::CoInitialize (NULL);}
	~CCom () {::CoUninitialize ();}
};

////////////////////////////////////

//
// Win32-related wrappers.
//

class CWinHandle {
	// Disable copy semantics.
	CWinHandle (const CWinHandle &);
	void operator = (const CWinHandle &);

  public:
	CWinHandle (HANDLE _h) : m_h (_h) {}
	~CWinHandle () {Close ();}

	void Close () {
		if (IsValid ())
			::CloseHandle (m_h);
		m_h = NULL;
	}

	HANDLE Detach () {
		const HANDLE h = m_h;
		m_h = NULL;
		return h;
	}

	bool IsValid () const {return m_h && m_h != INVALID_HANDLE_VALUE;}

	operator HANDLE () const {return m_h;}

	void operator = (HANDLE _h) {
		Close ();
		m_h = _h;
	}

  protected:
	HANDLE m_h;
};

class CFile : public CWinHandle {
  public:
	CFile (const char * _strFileName, DWORD _dwAccess, DWORD _dwShareMode, DWORD _dwCreationDisposition, DWORD _dwFlagsAndAttributes) :
		CWinHandle (::CreateFile (_strFileName, _dwAccess, _dwShareMode, NULL, _dwCreationDisposition, _dwFlagsAndAttributes, NULL))
	{}
};

class CFileForRead : public CFile {
  public:
	CFileForRead (const char * _strFileName) : CFile (_strFileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN) {}
};

class CFileForWrite : public CFile {
  public:
	CFileForWrite (const char * _strFileName) : CFile (_strFileName, GENERIC_READ | GENERIC_WRITE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN) {}
};

////////////////////////////////////////

class CCritSec : public CRITICAL_SECTION {
	// Disable copy semantics.
	CCritSec (const CCritSec &);
	void operator= (const CCritSec &);

  public:
	CCritSec () {::InitializeCriticalSection (this);}
	~CCritSec () {::DeleteCriticalSection (this);}

	void Lock   () {::EnterCriticalSection (this);}
	void Unlock () {::LeaveCriticalSection (this);}

	class CLock {
	  public:
		CLock (CCritSec & _cs) : m_cs (_cs) {m_cs.Lock ();}
		~CLock () {m_cs.Unlock ();}

	  private:
		CCritSec & m_cs;
	};
};

////////////////////////////////////

//
// GDI-related wrappers.
//

class CClientDC {
	// Disable copy semantics.
	CClientDC (const CClientDC &);
	void operator = (const CClientDC &);

  public:
	CClientDC (HWND _hWnd) :
		m_hWnd (_hWnd),
		m_hDC (::GetDC (_hWnd))
	{}
	~CClientDC () {if (m_hDC) ::ReleaseDC (m_hWnd, m_hDC);}

	operator HDC () const {return m_hDC;}

  private:
	const HWND m_hWnd;
	const HDC m_hDC;
};

class CDCHolder {
	// Disable copy semantics.
	CDCHolder (const CDCHolder &);
	void operator = (const CDCHolder &);

  public:
	CDCHolder (HDC _hDC) : m_hDC (_hDC) {}
	~CDCHolder () {if (m_hDC) ::DeleteDC (m_hDC);}

	operator HDC () const {return m_hDC;}

	HDC Detach () {
		const HDC hDC = m_hDC;
		m_hDC = NULL;
		return hDC;
	}

  private:
	HDC m_hDC;
};

class CDCSaver {
	// Disable copy semantics.
	CDCSaver (const CDCSaver &);
	void operator = (const CDCSaver &);

  public:
	CDCSaver (HDC _hdc) : m_hdc (_hdc), m_iCtx (::SaveDC (_hdc)) {}
	~CDCSaver () {::RestoreDC (m_hdc, m_iCtx);}

  private:
	HDC m_hdc;
	const int m_iCtx;
};

class CGdiObjectHolder {
	// Disable copy semantics.
	CGdiObjectHolder (const CGdiObjectHolder &);
	void operator = (const CGdiObjectHolder &);

  public:
	CGdiObjectHolder (HGDIOBJ _h) : m_h (_h) {}
	~CGdiObjectHolder () {if (m_h) ::DeleteObject (m_h);}

  private:
	HGDIOBJ m_h;
};

class CBitmapHolder {
	// Disable copy semantics.
	CBitmapHolder (const CBitmapHolder &);
	void operator = (const CBitmapHolder &);

  public:
	CBitmapHolder (HBITMAP _hBmp) : m_hBmp (_hBmp) {}
	~CBitmapHolder () {if (m_hBmp) ::DeleteObject (m_hBmp);}

	operator HBITMAP () const {return m_hBmp;}

	HBITMAP Detach () {
		const HBITMAP hBmp = m_hBmp;
		m_hBmp = NULL;
		return hBmp;
	}

  private:
	HBITMAP m_hBmp;
};

class CMemDC {
	// Disable copy semantics.
	CMemDC (const CMemDC &);
	void operator = (const CMemDC &);

  public:
	CMemDC::CMemDC (HDC _hdc, int _w, int _h) :
		m_hdcMem (::CreateCompatibleDC (_hdc)),
		m_hbmp (::CreateCompatibleBitmap (_hdc, _w, _h)),
		m_hbmpPrev (NULL)
	{
		SelectBmp ();
	}

	CMemDC::~CMemDC () {
		UnselectBmp ();

		if (m_hdcMem)
			::DeleteDC (m_hdcMem);
		if (m_hbmp)
			::DeleteObject (m_hbmp);
	}

	operator HDC () const {return m_hdcMem;}
	HBITMAP GetBitmap () const {return m_hbmp;}

	void CMemDC::SelectBmp () {
		if (m_hdcMem && m_hbmp)
			m_hbmpPrev = (HBITMAP) ::SelectObject (m_hdcMem, m_hbmp);
	}

	void CMemDC::UnselectBmp () {
		if (m_hdcMem && m_hbmpPrev) {
			::SelectObject (m_hdcMem, m_hbmpPrev);
			m_hbmpPrev = NULL;
		}
	}

	HBITMAP DetachBitmap () {
		UnselectBmp ();
		const HBITMAP hbmp = m_hbmp;
		m_hbmp = NULL;
		return hbmp;
	}

  private:
	const HDC m_hdcMem;
	HBITMAP m_hbmp;
	HBITMAP m_hbmpPrev;
};

# endif // __Wrappers_H_